package org.ktpro.shortlink.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.ktpro.shortlink.admin.common.biz.user.UserContext;
import org.ktpro.shortlink.admin.common.convention.exception.ClientException;
import org.ktpro.shortlink.admin.dao.entity.GroupDO;
import org.ktpro.shortlink.admin.dao.mapper.GroupMapper;
import org.ktpro.shortlink.admin.dto.req.ShortLinkGroupSortReqDTO;
import org.ktpro.shortlink.admin.dto.req.ShortLinkGroupUpdateReqDTO;
import org.ktpro.shortlink.admin.dto.resp.ShortLinkGroupRespDTO;
import org.ktpro.shortlink.admin.remote.dto.ShortLinkFeignRemoteService;
import org.ktpro.shortlink.admin.remote.dto.resp.ShortLinkGroupCountQueryRespDTO;
import org.ktpro.shortlink.admin.service.GroupService;
import org.ktpro.shortlink.admin.toolkit.RandomCodeGenerator;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static org.ktpro.shortlink.admin.common.constant.RedisCacheConstant.LOCK_GROUP_CREATE_KEY;

/**
 * 短链接接口实现层
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class GroupServiceImpl extends ServiceImpl<GroupMapper, GroupDO> implements GroupService {

    /**
     * 远程服务调用
     */
    private final ShortLinkFeignRemoteService shortLinkFeignRemoteService;
    private final RedissonClient redissonClient;

    @Value("${short-link.group.max_num}")
    private int groupMaxNum;

    /**
     * 短链接新增分组
     */
    public void saveGroup(String groupName) {
        RLock lock = redissonClient.getLock(String.format(LOCK_GROUP_CREATE_KEY, UserContext.getUsername()));
        lock.lock();
        try {
            //查询分组数量是否超过最大数量
            LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class)
                    .eq(GroupDO::getUsername, UserContext.getUsername())
                    .eq(GroupDO::getDelFlag, 0);  //未删除
            if (UserContext.getUsername() == null) {
                log.error("[ERROR] 用户名为空！");
                throw new RuntimeException("用户名为空，无法创建分组");
            }
            List<GroupDO> groups = baseMapper.selectList(queryWrapper);
            if (CollUtil.isNotEmpty(groups) && groups.size() == groupMaxNum) {
                throw new ClientException(String.format("已超出最大分组数：%d", groupMaxNum));
            }
            String randomGid;
            //找到一个不重复的可用gid
            do {
                randomGid = RandomCodeGenerator.generateRandomCode(6);
            } while (!hasGid(randomGid));

            GroupDO groupDO = GroupDO.builder()
                    .gid(randomGid)
                    .sortOrder(0)
                    .name(groupName)
                    .username(UserContext.getUsername())
                    .build();
            baseMapper.insert(groupDO);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 查询短链接分组
     */
    @Override
    public List<ShortLinkGroupRespDTO> listGroup() {
        //目的是获取分组信息，以及分组下的短链接数量;  其中短链接数量需要调用远程服务
        LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class)
                .eq(GroupDO::getUsername, UserContext.getUsername())
                .eq(GroupDO::getDelFlag, 0)  //未删除
                .orderByDesc(GroupDO::getSortOrder)
                .orderByDesc(GroupDO::getUpdateTime);
        List<GroupDO> groups = baseMapper.selectList(queryWrapper);
        List<ShortLinkGroupCountQueryRespDTO> gids = shortLinkFeignRemoteService.listGroupShortLinkCount(groups.stream().map(GroupDO::getGid).toList()).getData();
        List<ShortLinkGroupRespDTO> results = BeanUtil.copyToList(groups, ShortLinkGroupRespDTO.class);

        if(results == null || results.isEmpty()) {
            throw new ClientException("无分组信息，查询失败");
        }

        //通过stream流构建 gid 和 count的映射关系，即键值对，放到一个Map里
        Map<String, Integer> counts = gids.stream().collect(Collectors.toMap(ShortLinkGroupCountQueryRespDTO::getGid, ShortLinkGroupCountQueryRespDTO::getShortLinkCount));

        //在返回体的每个对象中填充一个短链接数量。  原理：通过gid进行匹配，获取对应的value（上面的短链接数量），通过set方法，给Stream流中每个实体赋值。
        return results.stream().peek(result -> result.setShortLinkCount(counts.getOrDefault(result.getGid(), 0))).toList();
    }

    /**
     * 修改分组信息  名称
     */
    @Override
    public void updateGroup(ShortLinkGroupUpdateReqDTO requestParam) {
        String gid1 = requestParam.getGid();
        //注意mybatis-plus的update语句写法
        LambdaUpdateWrapper<GroupDO> updateWrapper = Wrappers.lambdaUpdate(GroupDO.class)
                .eq(GroupDO::getUsername, UserContext.getUsername())
                .eq(GroupDO::getGid, gid1)
                .eq(GroupDO::getDelFlag, 0);
        GroupDO groupDO = new GroupDO();
        groupDO.setName(requestParam.getName());
        baseMapper.update(groupDO, updateWrapper);
    }

    /**
     * 删除短链接分组
     *
     * @param gid 分组标识
     */
    @Override
    public void deleteGroup(String gid) {
        //并不是真删除，而是更新删除标识，为了后续回收站功能

        LambdaUpdateWrapper<GroupDO> updateWrapper = Wrappers.lambdaUpdate(GroupDO.class)
                .eq(GroupDO::getUsername, UserContext.getUsername())
                .eq(GroupDO::getGid, gid)
                .eq(GroupDO::getDelFlag, 0);
        GroupDO groupDO = new GroupDO();
        groupDO.setDelFlag(1);
        baseMapper.update(groupDO, updateWrapper);
    }

    /**
     * 分组拖动排序
     */
    @Override
    public void sortGroup(List<ShortLinkGroupSortReqDTO> requestParam) {
        //修改现有的sortOder即可
        requestParam.forEach(each -> {
            String gid = each.getGid();
            Integer newOrder = each.getSortOrder();
            //更新媒介实体
            GroupDO groupDO = GroupDO.builder()
                    .sortOrder(newOrder)
                    .build();
            //MP的更新wrappers
            LambdaUpdateWrapper<GroupDO> updateWrapper = Wrappers.lambdaUpdate(GroupDO.class)
                    .eq(GroupDO::getUsername, UserContext.getUsername())
                    .eq(GroupDO::getGid, gid)
                    .eq(GroupDO::getDelFlag, 0);
            baseMapper.update(groupDO, updateWrapper);
        });
    }

    /**
     * 判断gid是否可用
     */
    private Boolean hasGid(String gid) {
        LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class)
                .eq(GroupDO::getGid, gid)
                .eq(GroupDO::getUsername, UserContext.getUsername());
        GroupDO groupDO = baseMapper.selectOne(queryWrapper);
        return groupDO == null;
    }
}
